package org.jpropelleralt.list.impl;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.jpropelleralt.change.Change;
import org.jpropelleralt.list.ListChange;
import org.jpropelleralt.list.ListDelta;
import org.jpropelleralt.utils.impl.UnmodifiableIterator;

/**
 * Immutable default implementation of {@link ListChange}
 */
public class ListChangeDefault implements ListChange {

	private final boolean shallow;
	private final List<ListDelta> listDeltas;

	/**
	 * Make a new {@link ListChangeDefault}
	 * @param shallow 	True if any instance in the list's data 
	 * 					is different after the change
	 * @param listDeltas	The deltas that have occurred to the list
	 */
	private ListChangeDefault(boolean shallow, List<ListDelta> listDeltas) {
		super();
		this.shallow = shallow;
		this.listDeltas = listDeltas;
	}

	/**
	 * Make a new {@link ListChangeDefault} with a single delta
	 * @param shallow 	True if any instance in the list's data 
	 * 					is different after the change
	 * @param delta		The delta that has occurred to the list
	 */
	private ListChangeDefault(boolean shallow, ListDelta delta) {
		this(shallow, makeSingleList(delta));
	}
	
	/**
	 * Create a shallow change with a single {@link ListDelta}
	 * @param delta		The {@link ListDelta}
	 * @return			The new change
	 */
	public static ListChangeDefault shallow(ListDelta delta) {
		return new ListChangeDefault(true, delta);
	}
	
	/**
	 * Create a deep change with a single {@link ListDelta} showing
	 * a complete alteration
	 * @param l 		The {@link List} that has changed
	 * @return			The new change
	 */
	public static ListChangeDefault deep(List<?> l) {
		return new ListChangeDefault(false, ListDeltaDefault.newEntireListAlteration(l));
	}
	
	private static List<ListDelta> makeSingleList(ListDelta delta) {
		List<ListDelta> list = new LinkedList<ListDelta>();
		list.add(delta);
		return Collections.unmodifiableList(list);
	}
	
	@Override
	public Change extend(Change existing) {
		
		if (!(existing instanceof ListChange)) {
			throw new IllegalArgumentException("A ListChange can only extend another Listchange");
		}
		
		ListChange listExisting = (ListChange)existing;
		
		//Extend different aspects of the change
		boolean extendsShallow = shallow() || existing.shallow();
		
		//Extend the delta list by adding our own deltas to the end
		LinkedList<ListDelta> extendsDeltas = new LinkedList<ListDelta>();
		for (ListDelta delta : listExisting) {
			extendsDeltas.add(delta);
		}
		for (ListDelta delta : this) {
			extendsDeltas.add(delta);
		}
		return new ListChangeDefault(extendsShallow, extendsDeltas);
	}

	@Override
	public boolean shallow() {
		return shallow;
	}

	@Override
	public String toString() {
		return (shallow ? "Shallow ":"Deep ") + "List Change";
	}

	@Override
	public Iterator<ListDelta> iterator() {
		return new UnmodifiableIterator<ListDelta>(listDeltas.iterator());
	}
	
}
